/***************************************************************************
 *   oboe.controllers.PerlinTerrain					*
 *   Copyright (C) 2011 by Felipe Manga					*
 *   lufeboma@gmail.com							*
 *									 *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the	   *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to	*
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:						 *
 *									 *
 *   The above copyright notice and this permission notice shall be	*
 *   included in all copies or substantial portions of the Software.	   *
 *									 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,	   *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF	*
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR	 *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.					   *
 ***************************************************************************/

package oboe.controllers;

import oboe.math.Number2D;
import oboe.math.Number3D;
import oboe.utils.RandomInt;

import flash.Vector;

class PerlinTerrain extends GridController
{
private var heightMap:Vector<Float>;
private var heightMapWidth:Int;
private static inline var tmp:Float3D = new Number3D(0,0,0);

public function new( tiles:Float2D, keys:Float2D, textureCount:Int, textureSize:Int )
{
	setupHeightMap( tiles, keys, 1/textureCount );
	super( heightMap, tiles, new Number2D(1,1), textureCount, textureSize, false );
	
	for( var y:Int = 0; y < tiles.y; ++y )
	{
	for( var x:Int = 0; x < tiles.x; ++x )
	{
		this.setTileTexture( x, y, int(this.getTileHeightQuick(x, y)*textureCount) );
	}
	}
}

private function setupHeightMap( tiles:Float2D, keys:Float2D, waterLevel:Float ):Void
{
	var range:Float2D = new Number2D( Number.MAX_VALUE, Number.MIN_VALUE );
	RandomInt.srand( keys.x*0xFFFF+keys.y );
	tiles.floor();
	this.heightMapWidth = tiles.x+1;
	heightMap = new Vector<Float>( (tiles.x+1)*(tiles.y+1), true );
	for( var y:Int = 0; y <= tiles.y; ++y )
	{
	for( var x:Int = 0; x <= tiles.x; ++x )
	{
		this.heightMap[ y*this.heightMapWidth+x ] = 0;
	}
	}
//		this.heightMap[ 0 ] = 1;
//		this.heightMap[ 1 ] = 1;
//		this.heightMap[ 3*tiles.x ] = 1;
//		return;
	
	perlin( 1, 0,0, tiles.x+1, tiles.y+1, range );
	
	var f:Float = 1/(range.y - range.x);
	if( waterLevel < 1 )
	{
	for( y = 0; y <= tiles.y; ++y )
	{
		for( x = 0; x <= tiles.x; ++x )
		{
		var h:Float = this.heightMap[ y*this.heightMapWidth+x ];
		h = (h-range.x) * f;
		if( h < waterLevel ) h = 0;
		else h = (h-waterLevel)/(1-waterLevel);
		this.heightMap[ y*this.heightMapWidth+x ] = h;
		}
	}
	}
	else
	{
	for( y = 0; y <= tiles.y; ++y )
	{
		for( x = 0; x <= tiles.x; ++x )
		{
		h = this.heightMap[ y*this.heightMapWidth+x ];
		h = (h-range.x) * f;
		this.heightMap[ y*this.heightMapWidth+x ] = h;
		}
	}
	}
}

private function perlin( it:Int, minX:Int, minY:Int, maxX:Int, maxY:Int, range:Float2D ):Void
{
	var r:Float;
	var t:Float;
	r = (RandomInt.randf())/(it); // *Math.sin(1.5708/(it*it));
	t = this.heightMap[ minY*this.heightMapWidth+minX ] + r;
	
	for( var y:Int = minY; y < maxY; ++y )
	{
	for( var x:Int = minX; x < maxX; ++x )
	{
		this.heightMap[ y*this.heightMapWidth+x ] = t;  
	}
	}
	++it;
	x = (minX+maxX)*0.5;
	y = (minY+maxY)*0.5;
	if( x <= minX || y <= minY )
	{
	if( t > range.y ) range.y = t;
	if( t < range.x ) range.x = t;
	return;
	}
	perlin( it, minX, minY, x, y, range );
	perlin( it, minX, y, x, maxY, range );
	perlin( it, x, minY, maxX, y, range );
	perlin( it, x, y, maxX, maxY, range );
}
}